package cn.net.yugu.doraemon.api.common.geo;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Objects;

import lombok.Getter;

/**
 * 地理坐标: 点
 *
 * @author xiangzr
 * @version 2020/4/28 18:13:46
 */
@Getter
public class CoordinatePoint implements Serializable {

  private static final long serialVersionUID = -8139803135054998578L;

  /**
   * 南纬90度
   */
  public static final BigDecimal LATITUDE_MIN_VALUE = new BigDecimal(-90);

  /**
   * 北纬90度
   */
  public static final BigDecimal LATITUDE_MAX_VALUE = new BigDecimal(90);

  /**
   * 西经180度
   */
  public static final BigDecimal LONGITUDE_MIN_VALUE = new BigDecimal(-180);

  /**
   * 东经180度
   */
  public static final BigDecimal LONGITUDE_MAX_VALUE = new BigDecimal(180);

  /**
   * 纬度
   * Decimal degrees (DD) express
   */
  private final BigDecimal latitude;

  /**
   * 经度
   * Decimal degrees (DD) express
   */
  private final BigDecimal longitude;

  /**
   * 是否为有效值
   *
   * @return true 有效值 ，false 无效值
   */
  public boolean valid() {
    if (Objects.isNull(latitude)) {
      return false;
    }
    if (Objects.isNull(longitude)) {
      return false;
    }
    if (latitude.compareTo(LATITUDE_MIN_VALUE) < 0) {
      return false;
    }

    if (latitude.compareTo(LATITUDE_MAX_VALUE) > 0) {
      return false;
    }

    if (longitude.compareTo(LONGITUDE_MIN_VALUE) < 0) {
      return false;
    }

    if (longitude.compareTo(LONGITUDE_MAX_VALUE) > 0) {
      return false;
    }

    return true;
  }

  public String getLatitudeAsText() {
    return latitude.toPlainString();
  }

  public String getLongitudeAsText() {
    return longitude.toPlainString();
  }

  public CoordinatePoint(String latitude, String longitude) {
    this.latitude = new BigDecimal(latitude);
    this.longitude = new BigDecimal(longitude);
    check();
  }

  public CoordinatePoint(Double latitude, Double longitude) {
    this.latitude = BigDecimal.valueOf(latitude);
    this.longitude = BigDecimal.valueOf(longitude);
    check();
  }

  public CoordinatePoint(BigDecimal latitude, BigDecimal longitude) {
    this.latitude = latitude;
    this.longitude = longitude;
    check();
  }

  private void check(){
    boolean valid = valid();
    if (!valid) {
      throw new IllegalArgumentException("输入的经纬度非法:[" + latitude + "," + longitude + "]");
    }
  }

  /**
   * 毫米级精度，精确到小数点后8位，存储时使用该精度数值即可，更大精度无保留必要
   * （已贴近当前GPS系统定位原理上的极限精度）
   * @return
   */
  public CoordinatePoint truncate(){
    return truncateToScale(8);
  }

  /**
   *
   * @return
   */
  public CoordinatePoint truncateToMeter(){
    return truncateToScale(5);
  }

  /**
   * 10米级精度。
   * 从民用设备的全球定位系统精度来看，一般场景下使用保留四位的值即可，更小精度无意义。
   * @return
   */
  public CoordinatePoint truncateToTenMeter(){
    return truncateToScale(4);
  }

  public CoordinatePoint truncateToHunMeter(){
    return truncateToScale(3);
  }

  public CoordinatePoint truncateToKm(){
    return truncateToScale(2);
  }

  public CoordinatePoint truncateToScale(int newScale){
    BigDecimal latitudeMeter = this.latitude.setScale(newScale, BigDecimal.ROUND_HALF_DOWN);
    BigDecimal longitudeMeter = this.longitude.setScale(newScale, BigDecimal.ROUND_HALF_DOWN);
    return new CoordinatePoint(latitudeMeter, longitudeMeter);
  }

  public double latDoubleValue(){
    return this.latitude.doubleValue();
  }

  public double lonDoubleValue(){
    return this.longitude.doubleValue();
  }
}
